using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
using UnityMcpBridge.Editor.Helpers;
using UnityMcpBridge.Editor.Models;
using UnityMcpBridge.Editor.Tools;

namespace UnityMcpBridge.Editor
{
    [InitializeOnLoad]
    public static partial class UnityMcpBridge
    {
        private static UdpClient listener;
        private static bool isRunning = false;
        private static readonly object lockObj = new();
        private static Dictionary<
            string,
            (string commandJson, TaskCompletionSource<string> tcs)
        > commandQueue = new();
        private static readonly int unityPort = 6400; // Hardcoded port

        public static bool IsRunning => isRunning;
        private static Thread listenerThread;

        public static bool FolderExists(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return false;
            }

            if (path.Equals("Assets", StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }

            string fullPath = Path.Combine(
                Application.dataPath,
                path.StartsWith("Assets/") ? path[7..] : path
            );
            return Directory.Exists(fullPath);
        }

        static UnityMcpBridge()
        {
            Start();
            EditorApplication.quitting += Stop;
            AssemblyReloadEvents.beforeAssemblyReload += () =>
            {
                Stop();
            };
        }

        public static void Start()
        {
            Stop();

            try
            {
                ServerInstaller.EnsureServerInstalled();
            }
            catch (Exception ex)
            {
                Debug.LogError($"Failed to ensure UnityMcpServer is installed: {ex.Message}");
            }

            if (isRunning)
            {
                return;
            }

            try
            {
                listener = new UdpClient(unityPort);
                isRunning = true;
                Debug.Log($"UnityMcpBridge started on port {unityPort} (UDP).");
                //Task.Run(ListenerLoop);
                // 创建并启动监听线程
                listenerThread = new Thread(ListenerLoop)
                {
                    IsBackground = true,
                    Name = "McpBridgeListener"
                };
                listenerThread.Start();
                EditorApplication.update += ProcessCommands;
            }
            catch (SocketException ex)
            {
                if (ex.SocketErrorCode == SocketError.AddressAlreadyInUse)
                {
                    Debug.LogError(
                        $"Port {unityPort} is already in use. Ensure no other instances are running or change the port."
                    );
                }
                else
                {
                    Debug.LogError($"Failed to start UDP listener: {ex.Message}");
                }
            }
        }

        public static void Stop()
        {
            if (!isRunning)
            {
                return;
            }

            try
            {
                isRunning = false;

                // 强制终止线程
                if (listenerThread != null && listenerThread.IsAlive)
                {
                    listenerThread.Abort();
                    listenerThread = null;
                }
                listener?.Close();
                listener?.Client?.Close();
                listener?.Client?.Dispose();
                listener = null;
                EditorApplication.update -= ProcessCommands;
                Debug.Log("UnityMcpBridge stopped.");
            }
            catch (Exception ex)
            {
                Debug.LogError($"Error stopping UnityMcpBridge: {ex}");
            }
        }
       

        // private static async Task ListenerLoop()
        // {
        //     while (isRunning)
        //     {
        //         try
        //         {
        //             var receivedResult = await listener.ReceiveAsync();
        //             _ = HandleDatagramAsync(receivedResult.Buffer, receivedResult.RemoteEndPoint);
        //         }
        //         catch (ObjectDisposedException)
        //         {
        //             // Listener was closed
        //         }
        //         catch (Exception ex)
        //         {
        //             if (isRunning)
        //             {
        //                 Debug.LogError($"Listener error: {ex.Message}");
        //             }
        //         }
        //     }
        // }

        private static void HandleDatagram(byte[] data, IPEndPoint remoteEndPoint)
        {
            try
            {
                string commandText = System.Text.Encoding.UTF8.GetString(data);
                string commandId = Guid.NewGuid().ToString();
                var tcs = new TaskCompletionSource<string>();

                // Ping直接响应处理
                if (commandText.Trim() == "ping")
                {
                    byte[] pingResponse = System.Text.Encoding.UTF8.GetBytes(
                        "{\"status\":\"success\",\"result\":{\"message\":\"pong\"}}"
                    );
                    listener.Send(pingResponse, pingResponse.Length, remoteEndPoint);
                    return;
                }

                lock (lockObj)
                {
                    commandQueue[commandId] = (commandText, tcs);
                }

                // 同步等待结果
                string response = tcs.Task.GetAwaiter().GetResult();
                byte[] responseBytes = System.Text.Encoding.UTF8.GetBytes(response);
                listener.Send(responseBytes, responseBytes.Length, remoteEndPoint);
            }
            catch (Exception ex)
            {
                Debug.LogError($"Datagram handler error: {ex.Message}");
            }
        }

        private static void ListenerLoop()
        {
            IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);

            while (isRunning)
            {
                try
                {
                    byte[] data = listener.Receive(ref remoteEP);
                    HandleDatagram(data, remoteEP); // 同步调用
                }
                catch (SocketException ex) when (ex.SocketErrorCode == SocketError.Interrupted)
                {
                    break;
                }
                catch (ThreadAbortException)
                {
                    Thread.ResetAbort();
                    break;
                }
                catch (Exception ex)
                {
                    if (isRunning)
                    {
                        Debug.LogError($"Listener error: {ex.Message}");
                    }
                }
            }
        }

        private static void ProcessCommands()
        {
            List<string> processedIds = new();
            lock (lockObj)
            {
                foreach (
                    KeyValuePair<
                        string,
                        (string commandJson, TaskCompletionSource<string> tcs)
                    > kvp in commandQueue.ToList()
                )
                {
                    string id = kvp.Key;
                    string commandText = kvp.Value.commandJson;
                    TaskCompletionSource<string> tcs = kvp.Value.tcs;

                    try
                    {
                        if (string.IsNullOrEmpty(commandText))
                        {
                            var emptyResponse = new
                            {
                                status = "error",
                                error = "Empty command received",
                            };
                            tcs.SetResult(JsonConvert.SerializeObject(emptyResponse));
                            processedIds.Add(id);
                            continue;
                        }

                        commandText = commandText.Trim();

                        if (commandText == "ping")
                        {
                            var pingResponse = new
                            {
                                status = "success",
                                result = new { message = "pong" },
                            };
                            tcs.SetResult(JsonConvert.SerializeObject(pingResponse));
                            processedIds.Add(id);
                            continue;
                        }

                        if (!IsValidJson(commandText))
                        {
                            var invalidJsonResponse = new
                            {
                                status = "error",
                                error = "Invalid JSON format",
                                receivedText = commandText.Length > 50
                                    ? commandText[..50] + "..."
                                    : commandText,
                            };
                            tcs.SetResult(JsonConvert.SerializeObject(invalidJsonResponse));
                            processedIds.Add(id);
                            continue;
                        }

                        Command command = JsonConvert.DeserializeObject<Command>(commandText);
                        if (command == null)
                        {
                            var nullCommandResponse = new
                            {
                                status = "error",
                                error = "Command deserialized to null",
                                details = "The command was valid JSON but could not be deserialized to a Command object",
                            };
                            tcs.SetResult(JsonConvert.SerializeObject(nullCommandResponse));
                        }
                        else
                        {
                            string responseJson = ExecuteCommand(command);
                            tcs.SetResult(responseJson);
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.LogError($"Error processing command: {ex.Message}\n{ex.StackTrace}");

                        var response = new
                        {
                            status = "error",
                            error = ex.Message,
                            commandType = "Unknown (error during processing)",
                            receivedText = commandText?.Length > 50
                                ? commandText[..50] + "..."
                                : commandText,
                        };
                        string responseJson = JsonConvert.SerializeObject(response);
                        tcs.SetResult(responseJson);
                    }

                    processedIds.Add(id);
                }

                foreach (string id in processedIds)
                {
                    commandQueue.Remove(id);
                }
            }
        }

        private static bool IsValidJson(string text)
        {
            if (string.IsNullOrWhiteSpace(text))
            {
                return false;
            }

            text = text.Trim();
            if (
                (text.StartsWith("{") && text.EndsWith("}"))
                || (text.StartsWith("[") && text.EndsWith("]"))
            )
            {
                try
                {
                    JToken.Parse(text);
                    return true;
                }
                catch
                {
                    return false;
                }
            }

            return false;
        }

        private static string ExecuteCommand(Command command)
        {
            try
            {
                if (string.IsNullOrEmpty(command.type))
                {
                    var errorResponse = new
                    {
                        status = "error",
                        error = "Command type cannot be empty",
                        details = "A valid command type is required for processing",
                    };
                    return JsonConvert.SerializeObject(errorResponse);
                }

                if (command.type.Equals("ping", StringComparison.OrdinalIgnoreCase))
                {
                    var pingResponse = new
                    {
                        status = "success",
                        result = new { message = "pong" },
                    };
                    return JsonConvert.SerializeObject(pingResponse);
                }

                JObject paramsObject = command.@params ?? new JObject();

                object result = command.type switch
                {
                    "manage_script" => ManageScript.HandleCommand(paramsObject),
                    "manage_scene" => ManageScene.HandleCommand(paramsObject),
                    "manage_editor" => ManageEditor.HandleCommand(paramsObject),
                    "manage_gameobject" => ManageGameObject.HandleCommand(paramsObject),
                    "manage_asset" => ManageAsset.HandleCommand(paramsObject),
                    "read_console" => ReadConsole.HandleCommand(paramsObject),
                    "execute_menu_item" => ExecuteMenuItem.HandleCommand(paramsObject),
                    _ => throw new ArgumentException(
                        $"Unknown or unsupported command type: {command.type}"
                    ),
                };

                var response = new { status = "success", result };
                return JsonConvert.SerializeObject(response);
            }
            catch (Exception ex)
            {
                Debug.LogError(
                    $"Error executing command '{command?.type ?? "Unknown"}': {ex.Message}\n{ex.StackTrace}"
                );

                var response = new
                {
                    status = "error",
                    error = ex.Message,
                    command = command?.type ?? "Unknown",
                    stackTrace = ex.StackTrace,
                    paramsSummary = command?.@params != null
                        ? GetParamsSummary(command.@params)
                        : "No parameters",
                };
                return JsonConvert.SerializeObject(response);
            }
        }

        private static string GetParamsSummary(JObject @params)
        {
            try
            {
                return @params == null || !@params.HasValues
                    ? "No parameters"
                    : string.Join(
                        ", ",
                        @params
                            .Properties()
                            .Select(static p =>
                                $"{p.Name}: {p.Value?.ToString()?[..Math.Min(20, p.Value?.ToString()?.Length ?? 0)]}"
                            )
                    );
            }
            catch
            {
                return "Could not summarize parameters";
            }
        }
    }
}